/*
 * Copyright (c) 2013-2018, Centre for Genomic Regulation (CRG).
 * Copyright (c) 2013-2018, Paolo Di Tommaso and the respective authors.
 *
 *   This file is part of 'Nextflow'.
 *
 *   Nextflow is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Nextflow is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Nextflow.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.upplication.s3fs.util;

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
 */
@SuppressWarnings("unchecked")
public class S3MultipartOptions<V extends S3MultipartOptions> {

    private static final Logger log = LoggerFactory.getLogger(S3MultipartOptions.class);

    public static final int DEFAULT_CHUNK_SIZE = 100 << 20;

    /**
     * Upload chunk max size
     */
    private int chunkSize;

    /**
     * Maximum number of threads allowed
     */
    private int maxThreads;

    /**
     * Maximum number of attempts to upload a chunk in a multiparts upload process
     */
    private int maxAttempts;

    /**
     * Time (milliseconds) to wait after a failed upload to retry a chunk upload
     */
    private long retrySleep;


    /**
     * initialize default values
     */
    {
        retrySleep = 500;
        chunkSize = DEFAULT_CHUNK_SIZE;
        maxAttempts = 5;
        maxThreads = Runtime.getRuntime().availableProcessors() *3;
    }

    public S3MultipartOptions() {

    }

    public S3MultipartOptions(Properties props) {
        setMaxThreads(props.getProperty("upload_max_threads"));
        setChunkSize(props.getProperty("upload_chunk_size"));
        setMaxAttempts(props.getProperty("upload_max_attempts"));
        setRetrySleep(props.getProperty("upload_retry_sleep"));
    }

    public int getChunkSize() {
        return chunkSize;
    }

    public int getChunkSize( long objectSize ) {
        final int MAX_PARTS = 10_000;
        long numOfParts = objectSize / chunkSize;
        if( numOfParts > MAX_PARTS ) {
            chunkSize = (int) objectSize / MAX_PARTS;
        }

        return chunkSize;
    }

    public int getMaxThreads() {
        return maxThreads;
    }

    public int getMaxAttempts() {
        return maxAttempts;
    }

    public long getRetrySleep() {
        return retrySleep;
    }


    public V setChunkSize(int chunkSize) {
        this.chunkSize = chunkSize;
        return (V)this;
    }

    public V setChunkSize(String chunkSize) {
        if( chunkSize==null )
            return (V)this;

        try {
            setChunkSize(Integer.parseInt(chunkSize));
        }
        catch( NumberFormatException e ) {
            log.warn("Not a valid AWS S3 multipart upload chunk size: `{}` -- Using default", chunkSize);
        }
        return (V)this;
    }

    public V setMaxThreads(int maxThreads) {
        this.maxThreads = maxThreads;
        return (V)this;
    }

    public V setMaxThreads(String maxThreads) {
        if( maxThreads==null )
            return (V)this;

        try {
            setMaxThreads(Integer.parseInt(maxThreads));
        }
        catch( NumberFormatException e ) {
            log.warn("Not a valid AWS S3 multipart upload max threads: `{}` -- Using default", maxThreads);
        }
        return (V)this;
    }

    public V setMaxAttempts(int maxAttempts) {
        this.maxAttempts = maxAttempts;
        return (V)this;
    }

    public V setMaxAttempts(String maxAttempts) {
        if( maxAttempts == null )
            return (V)this;

        try {
            this.maxAttempts = Integer.parseInt(maxAttempts);
        }
        catch(NumberFormatException e ) {
            log.warn("Not a valid AWS S3 multipart upload max attempts value: `{}` -- Using default", maxAttempts);
        }
        return (V)this;
    }

    public V setRetrySleep( long retrySleep ) {
        this.retrySleep = retrySleep;
        return (V)this;
    }

    public V setRetrySleep( String retrySleep ) {
        if( retrySleep == null )
            return (V)this;

        try {
            this.retrySleep = Long.parseLong(retrySleep);
        }
        catch (NumberFormatException e ) {
            log.warn("Not a valid AWS S3 multipart upload retry sleep value: `{}` -- Using default", retrySleep);
        }
        return (V)this;
    }

    public long getRetrySleepWithAttempt( int attempt ) {
        return retrySleep * ( 1 << (attempt-1) );
    }

    @Override
    public String toString() {
        return "chunkSize=" + chunkSize +
                "; maxThreads=" + maxThreads +
                "; maxAttempts=" + maxAttempts +
                "; retrySleep=" + retrySleep;
    }

}
